-
Notifications
You must be signed in to change notification settings - Fork 322
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Class StatesDocument #3902
Class StatesDocument #3902
Conversation
- Modified the names of the Trajectory methods to match the latest names in Component.h. - Reintroduced the precision argument in SimTK::Xml::Elment::setValueAs<T>.
Added a place holder for the Catch2 unit test for class StatesDocument. testStatesDocument.cpp is now being incorporated in the build, and the test case runs when "build" executed on the target "RUN_TESTS_PARALLEL".
- Moved all utility methods into a struct with local linkage. - Streamlined code by adding `getEltValue()`. Use of this method reduced some code repetition.
- Minor changes to line formatting.
- Minor corrections/additions to comments
- Added code borrowed from testComponentInterface.cpp to generate a dummy model with a variety of states. The code compiles and runs with all test cases passing, but this is just a starting point. De/serialization using class StatesDocument is not yet being tested.
- Removed code borrowed from testComponentInterface.cpp. That code was not the way to go. A legitimate model is needed. I'm starting over.
- Made small improvements and corrections in the documentation.
- Now building a simple model and running a simulation.
- Added code to serialize, deserialize, and then re-serialize the state trajectory recorded during a simulation. The code is functioning as expected!
- Changed member variable `m_states` from type std::vector<SimTK::State> to SimTK::Array_<SimTK::State> - Added `exportToStatesDocument()`
- Added a `get` method that provides access to the underlying SimTK::Array_<SimTK::State> member variable.
- Added a method that tests equality of 2 state trajectories. The method does not yet test equality for discrete variables or modeling options, but Qs, Us, and Zs are passing.
- Added a static method that computes maximum rounding error based on the specified precision and value that is rounded.
- Cleaned up the computation of max_eps.
- Added equality comparisons for discrete variables and modeling options.
- Added ExtendedPointToPointSpring.
Since the trajectory was changed from std::vector<State> to SimTK::Array_<State>, bounds checking behavior has changed. SimTK::Array_<T> only checks bounds in DEBUG. In other words, an IndexOutOfRange exception will not be thrown in a release build. To account for this, I inserted an #ifdef to skip bounds checking unless in DEBUG.
Added the following types: - bool - int - double - Vec2 - Vec3 - Vec4 - Vec5 - Vec6 All tests are passing, and the .ostates file looks right.
So that discrete variables can be updated during a simulation, class ExtendedPointToPointSpring is now built on top of class ExtendedTwoPointLinearSpring. ExtendedTwoPointLinearSpring allocates auto-update discrete variables in its own `realizeTopology()` and those discrete variables are updated in its own `realizePosition()`.
The discrete variables can be added in extendRealizeTopology(). And, they can be changed in computeForce().
- Changed the allocation for discrete variables to the DefaultSystemSubsystem. - Primarily using the subsystem index instead of the pointer to the subsystem. - Discrete variables are being changed during simulation in ExtendedPointToPointSpring::computeForce(). All checks are now passing!
The note is serialized and deserialized. All checks are passing.
1. Now looping over over output precision from precision = 2 to precision = 22. 2. Added the ability to change the name of a discrete state for the purpose of testing exceptions when a discrete state is not found.
@aymanhab Ok! I reverted back to The modifications to allow |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewed 2 of 8 files at r1, 1 of 2 files at r3, 1 of 1 files at r4, 2 of 6 files at r5, all commit messages.
Reviewable status: 6 of 10 files reviewed, 2 unresolved discussions (waiting on @fcanderson)
OpenSim/Simulation/StatesTrajectoryReporter.cpp
line 39 at r5 (raw file):
const std::vector<SimTK::State>& StatesTrajectoryReporter::getStateArray() const { return m_states.getStateArray();
Wonder if the name of the method should change?
OpenSim/Simulation/Test/testStatesTrajectory.cpp
line 580 at r5 (raw file):
// states[5]; //#endif
Any reason to keep these comments?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewable status: 5 of 10 files reviewed, 2 unresolved discussions (waiting on @aymanhab and @nickbianco)
OpenSim/Simulation/StatesTrajectoryReporter.cpp
line 39 at r5 (raw file):
Previously, aymanhab (Ayman Habib) wrote…
Wonder if the name of the method should change?
Ah, I think I see what you are suggesting. The return is now a vector<>
instead of a SimTK::Array_<>
.
I didn't want to use getStateTrajectory()
since there is a StateTrajectory
class.
The only reason I might not want to use getStateVector()
is that name will likely conjure the notion of an object that contains the states at a single time stamp. A SimTK::State
object itself is what some might think of as a state vector.
The name that I think is probably most accurate and least likely to be confused with some other entity in this arena is
getVectorOfStateObjects()
since an std::vector<>
is returned.
If you agree, I'll go ahead and change the method name to getVectorOfStateObjects()
.
OpenSim/Simulation/Test/testStatesTrajectory.cpp
line 580 at r5 (raw file):
Previously, aymanhab (Ayman Habib) wrote…
Any reason to keep these comments?
I do not know why I commented out these lines originally. So, I'm glad you spotted them. It likely had to do with the fact that testStatesTrajectory
fails for a Debug build. I might have thought that commenting out those lines would allow the test to run successfully. Not sure, but if I did, that was wrong.
I just tested the code with those lines commented out and with those lines restored. Either way, the Debug build fails because of an array index out of bounds exception that is thrown while executing
SimTK_SUBTEST1(testFromStatesStorageInconsistentModel, pre40StoFname); (testStatesTrajectory.cpp: line 799)
In a release build (either Release
or RelWithDebInfo
) the test passes because bounds checking is disabled.
Since I didn't write this test, the better thing to do restore the lines that I commented out. I'm not exactly sure what functionality they are testing.
As a general comment, there are a number of OpenSim tests that fail for a Debug
build. I haven't reported them because I suspected that you all (the devs) were aware of the failures and had decided to pass on them as non-critical. After talking with @nickbianco , however, it seems that maybe running the tests for a Debug
build isn't really part of the development cycle. So maybe you all are not aware of the Debug
test failures?
I intended to follow up with @nickbianco on this, but I just haven't had the resources to do it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A couple minor comments but otherwise looks great. My main question at the end of this was how would API users distinguish if they're using a Full state with discrete variable and modeling options vs. the traditional state that had only continuous variables. Maybe some documentation/disclaimers would do.
Reviewed 4 of 6 files at r5, 1 of 1 files at r6, all commit messages.
Reviewable status: all files reviewed, 5 unresolved discussions (waiting on @fcanderson)
OpenSim/Simulation/StatesDocument.h
line 221 at r6 (raw file):
significant figures would go unnoticed).
Amazingly clear description @fcanderson Thank you 🥇
OpenSim/Simulation/StatesDocument.cpp
line 435 at r6 (raw file):
// Many other aspects are checked for consistency than just the model // name. Those are more easily checked as the doc is parced.
parsed?
OpenSim/Simulation/StatesDocument.cpp
line 450 at r6 (raw file):
SimTK_ASSERT_ALWAYS(false, msg.c_str()); }
This maybe overstated on my side but we never gave the model name much significance, users easily modify model names in GUI or XML without thinking about downstream effects. Considering that the meat of consistency test is done elsewhere I wonder if you think this check is relevant here.
OpenSim/Simulation/StatesDocument.cpp
line 673 at r6 (raw file):
model, path, traj); } else {
These if/else(s) typically worry me because I don't know if we covered all cases. but with templates I guess that's the price to pay. Is there a master list of these types that we can refer to in case e.g. Arrays of Vectors, Matrices or SpatialVectors beome valid types?
OpenSim/Simulation/StatesTrajectoryReporter.cpp
line 39 at r5 (raw file):
Previously, fcanderson (Clay Anderson) wrote…
Ah, I think I see what you are suggesting. The return is now a
vector<>
instead of aSimTK::Array_<>
.I didn't want to use
getStateTrajectory()
since there is aStateTrajectory
class.The only reason I might not want to use
getStateVector()
is that name will likely conjure the notion of an object that contains the states at a single time stamp. ASimTK::State
object itself is what some might think of as a state vector.The name that I think is probably most accurate and least likely to be confused with some other entity in this arena is
getVectorOfStateObjects()
since an
std::vector<>
is returned.If you agree, I'll go ahead and change the method name to
getVectorOfStateObjects()
.
Agreed
Previously, aymanhab (Ayman Habib) wrote…
Thank you. I worked hard on the documentation. Understanding how states work in Simbody and in OpenSim is not generally well understood, even in the developer community. It was an opportunity to get a clear description out there. In addition, it was important to lay out the foundational need for a class like OpenSim::StatesDocument. |
Previously, aymanhab (Ayman Habib) wrote…
You are in a better position to make this call. In an effort to avoid a situation where a user would perform a detailed analysis with the wrong set of states, I opted to make the bar for compatibility high. If a user really would like to conduct an analysis with a particular .ostates file that has a model name that doesn't match, I thought opening the file and manually changing the file name, though tedious, was not too much to ask. You are familiar with use cases in the GUI, Matlab, etc. What do you suggest? |
Previously, aymanhab (Ayman Habib) wrote…
I thought about ways of avoiding the if/else(s), but did not come up with something that wasn't burdensome. OpenSim has been used for YEARS and no one until I came along with my contact class has complained about other types besides I thought when another such case comes along, we could quickly add another if/else for the type in demand. It really only means the addition of several lines of code. To clarify, in Simbody, any numerical type that can be represented by a In |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very close, I believe I responded to your questions, let's address the remaining items to get it over the hump. Thank you
Reviewable status: all files reviewed, 4 unresolved discussions (waiting on @fcanderson)
OpenSim/Simulation/StatesDocument.cpp
line 450 at r6 (raw file):
Previously, fcanderson (Clay Anderson) wrote…
You are in a better position to make this call.
In an effort to avoid a situation where a user would perform a detailed analysis with the wrong set of states, I opted to make the bar for compatibility high. If a user really would like to conduct an analysis with a particular .ostates file that has a model name that doesn't match, I thought opening the file and manually changing the file name, though tedious, was not too much to ask.
You are familiar with use cases in the GUI, Matlab, etc. What do you suggest?
I'll leave it as is and see if it becomes an issue in actual use cases, at this level of API use, more stringent is better.
OpenSim/Simulation/StatesDocument.cpp
line 673 at r6 (raw file):
Previously, fcanderson (Clay Anderson) wrote…
I thought about ways of avoiding the if/else(s), but did not come up with something that wasn't burdensome. OpenSim has been used for YEARS and no one until I came along with my contact class has complained about other types besides
double
being unavailable.I thought when another such case comes along, we could quickly add another if/else for the type in demand. It really only means the addition of several lines of code.
To clarify, in Simbody, any numerical type that can be represented by a
SimTK::AbstractValue()
is fair game for aSimTK::DiscreteVariable
.In
OpenSim::StatesDocument
, the supported types are listed in the documentation:
https://github.com/fcanderson/opensim-core/blob/ostates_2/OpenSim/Simulation/StatesDocument.h#L278
Thanks @fcanderson I'd add comment to the block in the cpp block so that future maintainers understand where this list came from and why.
The following answer may not address your question adequately. I find it challenging to answer. If I come up short, a Zoom call in which you and I can go back and forth about concepts might be the easiest way to proceed. API users should always assume that every instance of a For the purpose of state de/serialization, Historically, API programmers have taken those additional measures. When states other than the continuous states have been needed, those states have been saved in some ad hoc way. For example, as a The situation with my contact model targeted the Achilles' Heel of OpenSim. My contact model not only has input discrete states of various types (int, Vec3, etc) , it also has OUTPUT discrete states of different types! There just wasn't a viable way to handle this in OpenSim. This whole area has been hard to communicate about. Nick has an appreciation of the details; he performed a detailed review of the enhacements to I see the OpenSim workflow as migrating to something like:
I haven't seen the .ostates XML file as the end product from the user's point of view. I've seen it as a systematic and comprehensive means of getting at the information they desire. Because .ostates will always contain the full state, any piece of data concerning a model can be (re)generated. |
Previously, aymanhab (Ayman Habib) wrote…
agreed |
Previously, aymanhab (Ayman Habib) wrote…
Working on it. |
Previously, aymanhab (Ayman Habib) wrote…
Done. |
Previously, aymanhab (Ayman Habib) wrote…
Done. |
Updated the documentation to reflect the fact that std::vector<SimTK::State> is also an acceptable form of the state trajectory for interfacing with OpenSim.
Migrated StatesTrajectoryReporter::getStateArray() to getVectorOfStateObjects()
Migrated StatesTrajectoryReporter::getStateArray() to getVectorOfStateObjects().
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @fcanderson and I agree completely with your assessment regarding the holes in the current system before this PR, and appreciate the work you've done to enable this important functionality.
My question was regarding the StateTrjectory class/methods e.g. exportToTable has no way to tell where it came from, could it export discrete variables or modeling-options? Maybe it doesn't matter (that's a totally satisfactory resolution) or if not we could maintain a flag for book-keeping. Mainly asking for clarification for users who are already utilizing the StatesTrajectory vehicle elsewhere. If that's a more involved discussion or something that you resolved earlier or better done on Zoom I'm totally open. Thanks again.
Reviewed 4 of 4 files at r7, all commit messages.
Reviewable status: all files reviewed, 1 unresolved discussion (waiting on @fcanderson)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My understanding is that StatesTrajectory generally gets the states from a StatesTrajectoryReporter, in which case the trajectory of SimTK::State objects it contains will have valid values for all state categories (continuous, discrete, modeling). If, however, StatesTrajectory gets its states from a saved file, then any instance of a SimTK::State that is based only on reading that file will only have valid values for the Continuous states; the discrete variables and modeling options will be left uninitialized. Valid values of discrete and modeling states, in this case, will have to come from another source (e.g., another input file).
A flag in the StatesTrajectory to indicate whether it contains an entirely valid trajectory of SimTK::State objects seems like it could be a useful thing. If the origin of the trajectory was from a StatesTrajectoryReport, the flag is set to COMPLETE. On the other hand, if the origin of the trajectory was from file, then the flag is set to INCOMPLETE.
In its current form, StatesTrajectory doesn't export discrete variables or modeling options. It could be modified to do so, however. OpenSim::Component now has the infrastructure to efficiently get or set each state variable (continuous, discrete, or modeling) individually from or into a SimTK::Array_SimTK::State by specifying the variable's path. OpenSim::Component keeps an std::map for each state category using path as the hash index.
Enhancing StatesTrajectory would be a natural step to take except for one road block (that I am aware of). TimeSeriesTable does not currently have a way to co-mingle variables of different types. I really don't like the idea of serializing to multiple files, one for each type encountered (one for the int's, one for double's, one for Vec3's, etc.). That seems like a recipe for a logistical nightmare. How do you manage to keep track of all the files that go together?
The appeal of using XML, at least as for an initial pass at this functionality, is that the xml attribute mechanism was a really natural way to encode variable type. AND, SimTK::Xml already knows how to serialize and deserialize ALL possible variable types that could be encountered in Simbody (matrices, SpatialVec, etc). Using XML presented a clear path to getting all states in ONE file with a model name, appropriate precision, time stamp, and note.
If .ostate file size becomes an issue, I do think pursuing binary serialization would be a productive way to go. To get this functionality, augmenting the appropriate Simbody classes I think should be the point of attack. There would just need to be a flag that is set in SimTK::Xml to signal binary serialization, and each concrete SimTK::Value type would need to know how to stream itself in binary form. The functionality may actually already exist. I haven't looked into it.
Reviewable status: all files reviewed, 1 unresolved discussion (waiting on @fcanderson)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A follow-up:
- I read about binary XML formats. It appears like a much debated topic. Most people, it seems, are not in favor of binary because it removes one of the hallmark design characteristics of XML -- it is user readable and editable with common editors.
- A good alternative to using some kind of binary XML to reduce file size is using compression (e.g., zip'ing).
- In StatesTrajectory.h, I do make a disclaimer about using StatesTrajectory::createFromStorage() and createfromStatesTable() as a deserialization approach.
See https://github.com/fcanderson/opensim-core/blob/ostates_2/OpenSim/Simulation/StatesTrajectory.h#L314
Reviewable status: all files reviewed, 1 unresolved discussion (waiting on @fcanderson)
I added comments for developers should a demand additional supported variable types emerge.
Corrected a compile error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewable status: 8 of 10 files reviewed, 1 unresolved discussion (waiting on @aymanhab)
OpenSim/Simulation/StatesDocument.cpp
line 673 at r6 (raw file):
Previously, fcanderson (Clay Anderson) wrote…
Working on it.
Done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@aymanhab I believe I've addressed all the requested changes.
You'll notice that I pushed 1 or 2 more commits. I took the liberty of adjusting the documentation to reflect that both std::vecter<SimTK::State>
or SimTK::Array_<SimTK::State>
will work as in interface to class StatesDocument.
If all looks good to you, then it looks good to me.
Reviewable status: 8 of 10 files reviewed, 1 unresolved discussion (waiting on @aymanhab)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @fcanderson Looks great. Please update the changelog and we're done. Great work 👍
Reviewed 2 of 2 files at r8, all commit messages.
Reviewable status: complete! all files reviewed, all discussions resolved (waiting on @fcanderson)
@aymanhab I just updated CHANGELOG.md. Thank you very much, Ayman, for your review. I am glad to have this chunk of functionality in OpenSim. Now that auto-update discrete states of different variable types are supported in OpenSim, the next stop is getting the ExponentialContact class merged and seeing how it performs for real models not just simple test models. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewed 1 of 1 files at r9, all commit messages.
Reviewable status: complete! all files reviewed, all discussions resolved (waiting on @fcanderson)
Brief summary of changes
Class
StatesDocument
is an addition to OpenSim that adds the ability to serialize and deserialize a complete time history of model states to and from a single.ostates
file. This class fulfills a roadmap articulated about 10 years ago. See the bottom of StatesTrajectory.h.Prior to class
StateDocument
, only the continuous states (e.g., joint coordinates, joint speeds, etc.) were serialized in a systematic manner, leaving discrete states and modeling options unserialized. One of the challenges in serializing discrete states is that their type can vary (e.g.,bool
,int
,double
,Vec2
,Vec3
, etc.) and existing approaches for serializing states (e.g., storage files and time series tables) do not easily support data of mixed types.Class
StatesDocument
usesXML
(see classSimTK::Xml
) as the framework for serializing all state categories and all data types. It also contains serialization date stamps accounting for time zone, contains an annotation note, and supports serialization with a range of output precisions ( 1 to 20 digits), allowing the user to reduce the.ostates
file size or achieve lossless de/serialization, .Minor additions were made to class
StatesTrajectory
andStatesTrajectoryReporter
to support theStatesDocument
class. Note, however, that classStatesDocument
is build on low-levelSimTK
classes and the recently enhanced OpenSim::Component class, and does not have any knowledge of theStatesTrajectory
orStatesTrajectoryReporter
classes.For details concerning class
StatesDocument
, consult the Doxygen compliant comments in StatesDocument.h.Testing I've completed
Class
StatesDocument
is accompanied by a unit test. See testStatesDocument.cpp. This unit test exercises state serialization in all categories (continuous variables, discrete variables, and modeling options) and in all supported data types (bool
,int
,double
,Vec2
,Vec3
,Vec4
,Vec5
,Vec6
). All OpenSim unit tests are passing onWindows 10
for aRelWithDebInfo
build.Looking for feedback on...
StatesTrajectory
andStatesTrajectoryReporter
acceptable?CHANGELOG.md (choose one)
CHANGELOG.md has been updated.
This change is